/*------------------------------------------------------------
 *  Copyright 2009 By Peter Jamieson and Steve Wilton
 *  Copyright 2002 By Kara K.W. Poon and Steve Wilton
 *
 * Permission to use, copy, and modify this software and its documentation is
 * hereby granted only under the following terms and conditions.  Both the
 * above copyright notice and this permission notice must appear in all copies
 * of the software, derivative works or modified versions, and any portions
 * thereof, and both notices must appear in supporting documentation.
 * Permission is granted ONLY for non-commercial use.  For
 * commercial use, please contact the authors.
 *
 * This software may be distributed (but not offered for sale or transferred
 * for compensation) to third parties, provided such third parties agree to
 * abide by the terms and conditions of this notice.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS, AS
 * WELL AS THE UNIVERSITY OF BRITISH COLUMBIA AND THE
 * UNIVERSITY OF TORONTO DISCLAIM ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS.   INP NO EVENT SHALL THE
 * AUTHORS OR THE UNIVERSITY OF BRITISH COLUMBIA OR THE
 * UNIVERSITY OF TORONTO BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER INP AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUTP OF OR INP CONNECTION
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *------------------------------------------------------------*/
/*
  VPR was first written by Vaughn Betz at the University of
  Toronto. Descriptions on different versions of VPR is available
  in the descript.txt file.

  The power model is an extension of the original VPR. This
  feature is added by Kara Poon at the University of British
  Columbia as a master thesis project supervised by Dr. Steve
  Wilton.

  The extensions for VPR 5.0 were added by Peter Jamieson

 */
#include <string.h>
#include <assert.h>
#include "util.h"
#include "arch_types.h"
#include "ReadLine.h"
#include "ezxml.h"
#include "read_xml_arch_file.h"
#include "read_xml_util.h"

/* special type indexes, necessary for initialization initialization, everything afterwards
   should use the pointers to these type indices*/
#define EMPTY_TYPE_INDEX 0
#define IO_TYPE_INDEX 1
enum Fc_type
{ FC_ABS, FC_FRAC, FC_FULL };
extern struct s_type_descriptor *type_descriptors = NULL;
t_type_ptr IO_TYPE = NULL;
t_type_ptr EMPTY_TYPE = NULL;


/* Function prototypes */
static char **
GetNodeTokens (
    ezxml_t Node);
static void CountTokensInString (
    const char *Str,
    int *Num,
    int *Len);
static int CountChildren (
    ezxml_t Node,
    const char *Name);
static void ParseFc (
    ezxml_t Node,
    enum Fc_type *Fc,
    float *Val);
static void SetupEmptyType (
);
static void SetupClassInf (
    ezxml_t Classes,
    t_type_descriptor * Type);
static void SetupPinClasses (
    ezxml_t Classes,
    t_type_descriptor * Type);
static void SetupPinLocations (
    ezxml_t Locations,
    t_type_descriptor * Type);
static void SetupGridLocations (
    ezxml_t Locations,
    t_type_descriptor * Type);
static void SetupTypeTiming (
    ezxml_t timing,
    t_type_descriptor * Type);
static void SetupSubblocksTcomb (
    ezxml_t timing,
    t_type_descriptor * Type);
static void SetupSubblocksTSeq (
    ezxml_t timing_seq_in,
    ezxml_t timing_seq_out,
    t_type_descriptor * Type);
static void Process_Fc (
    ezxml_t Fc_in_node,
    ezxml_t Fc_out_node,
    t_type_descriptor * Type);
static void ProcessSubblocks (
    INOUTP ezxml_t Parent,
    INOUTP t_type_descriptor * Type,
    INP boolean timing_enabled);
static void ProcessTypeProps (
    ezxml_t Node,
    t_type_descriptor * Type);
static void ProcessChanWidthDistr (
    INOUTP ezxml_t Node,
    OUTP struct s_arch *arch);
static void ProcessChanWidthDistrDir (
    INOUTP ezxml_t Node,
    OUTP t_chan * chan);
static void ProcessLayout (
    INOUTP ezxml_t Node,
    OUTP struct s_arch *arch);
static void ProcessDevice (
    INOUTP ezxml_t Node,
    OUTP struct s_arch *arch,
    INP boolean timing_enabled);
static void ProcessIO (
    INOUTP ezxml_t Node,
    INP boolean timing_enabled);
static void ProcessTypes (
    INOUTP ezxml_t Node,
    OUTP t_type_descriptor ** Types,
    OUTP int *NumTypes,
    INP boolean timing_enabled);
static void ProcessSwitches (
    INOUTP ezxml_t Node,
    OUTP struct s_switch_inf **Switches,
    OUTP int *NumSwitches,
    INP boolean timing_enabled);
static void ProcessSegments (
    INOUTP ezxml_t Parent,
    OUTP struct s_segment_inf **Segs,
    OUTP int *NumSegs,
    INP struct s_switch_inf *Switches,
    INP int NumSwitches,
    INP boolean timing_enabled);
float ProcessCB_SB (
    INOUTP ezxml_t Node,
    INOUTP boolean * list,
    INP int len);
static void ProcessTempRecords (
    INOUTP ezxml_t Parent,
    OUTP t_power * power);
static void ProcessClocks (
    INOUTP ezxml_t Parent,
    OUTP t_clocks * clocks);
static void ProcessPower (
    INOUTP ezxml_t Parent,
    OUTP t_power * power);


/* Figures out the Fc type and value for the given node. Unlinks the
 * type and value. */
static void
ParseFc (
    ezxml_t Node,
    enum Fc_type *Fc,
    float *Val)
{
	const char *Prop;

	Prop = FindProperty (Node, "type", TRUE);
	if (0 == strcmp (Prop, "abs"))
	{
		*Fc = FC_ABS;
	}

	else if (0 == strcmp (Prop, "frac"))
	{
		*Fc = FC_FRAC;
	}

	else if (0 == strcmp (Prop, "full"))
	{
		*Fc = FC_FULL;
	}

	else
	{
		printf (ERRTAG "Invalid type '%s' for Fc. Only abs, frac " "and full are allowed.\n", Prop);
		exit (1);
	}
	switch (*Fc)
	{
		case FC_FULL:
			*Val = 0.0;
			break;
		case FC_ABS:
		case FC_FRAC:
			*Val = atof (Node->txt);
			ezxml_set_attr (Node, "type", NULL);
			ezxml_set_txt (Node, "");
			break;
		default:
			assert (0);
	}

	/* Release the property */
	ezxml_set_attr (Node, "type", NULL);
}

/* Counts number of child elements in a container element.
 * Name is the name of the child element.
 * Errors if no occurances found. */
static int
CountChildren (
    ezxml_t Node,
    const char *Name)
{
	ezxml_t Cur, sibling;
	int Count;

	Count = 0;
	Cur = Node->child;
	while (Cur)
	{
		if (strcmp (Cur->name, Name) == 0)
		{
			++Count;
		}
		sibling = Cur->sibling;
		Cur = Cur->next;
		if (Cur == NULL)
		{
			Cur = sibling;
		}
	}

	/* Error if no occurances found */
	if (Count < 1)
	{
		printf (ERRTAG "Expected node '%s' to have " "child elements, but none found.\n", Node->name);
		exit (1);
	}
	return Count;
}
/* Returns a token list of the text nodes of a given node. This
 * unlinks all the text nodes from the document */
static char **
GetNodeTokens (
    ezxml_t Node)
{
	int Count, Len;
	char *Cur, *Dst;

	boolean InToken;
	char **Tokens;


	/* Count the tokens and find length of token data */
	CountTokensInString (Node->txt, &Count, &Len);

	/* Error out if no tokens found */
	if (Count < 1)
	{
		printf (ERRTAG "Tag '%s' expected enclosed text data " "but none was found.\n", Node->name);
		exit (1);
	}
	Len = (sizeof (char) * Len) +	/* Length of actual data */
	      (sizeof (char) * Count);	/* Null terminators */

	/* Alloc the pointer list and data list. Count the final
	 * empty string we will use as list terminator */
	Tokens = (char **) my_malloc (sizeof (char *) * (Count + 1));
	Dst = (char *) my_malloc (sizeof (char) * Len);
	Count = 0;

	/* Copy data to tokens */
	Cur = Node->txt;
	InToken = FALSE;
	while (*Cur)
	{
		if (IsWhitespace (*Cur))
		{
			if (InToken)
			{
				*Dst = '\0';
				++Dst;
			}
			InToken = FALSE;
		}

		else
		{
			if (!InToken)
			{
				Tokens[Count] = Dst;
				++Count;
			}
			*Dst = *Cur;
			++Dst;
			InToken = TRUE;
		}
		++Cur;
	}
	if (InToken)
	{				/* Null term final token */
		*Dst = '\0';
		++Dst;
	}
	ezxml_set_txt (Node, "");
	Tokens[Count] = NULL;		/* End of tokens marker is a NULL */

	/* Return the list */
	return Tokens;
}


/* Count tokens and length in all the Text children nodes of current node. */
static void
CountTokensInString (
    const char *Str,
    int *Num,
    int *Len)
{
	boolean InToken;
	*Num = 0;
	*Len = 0;
	InToken = FALSE;
	while (*Str)
	{
		if (IsWhitespace (*Str))
		{
			InToken = FALSE;
		}

		else

		{
			if (!InToken)
			{
				++(*Num);
			}
			++(*Len);
			InToken = TRUE;
		}
		++Str;			/* Advance pointer */
	}
}
/* Set's up the classinf data of a type and counts the number of
 * pins the type has. If capacity > 1, we need multiple sets of classes.
 * This correctly sets num_pins to include all the replicated but
 * unique pins that give the capacity. */
static void
SetupClassInf (
    ezxml_t Classes,
    t_type_descriptor * Type)
{
	int i, k, CurClass, CurPin, NumClassPins;

	ezxml_t Cur;
	const char *Prop;


	/* Alloc class_inf structs */
	Type->num_class = Type->capacity * CountChildren (Classes, "class");
	Type->class_inf = (t_class *) my_malloc (Type->num_class * sizeof (t_class));

	/* Make multiple passes to handle capacity with index increased each pass */
	CurPin = 0;
	CurClass = 0;
	Type->num_pins = 0;
	Type->num_drivers = 0;
	Type->num_receivers = 0;
	for (i = 0; i < Type->capacity; ++i)
	{

		/* Restart the parse tree at each node */
		Cur = Classes->child;
		while (Cur)
		{
			CheckElement (Cur, "class");

			/* Count the number of pins in this class */
			CountTokensInString (Cur->txt, &NumClassPins, &k);
			Type->num_pins += NumClassPins;

			/* Alloc class structures */
			Type->class_inf[CurClass].num_pins = NumClassPins;
			Type->class_inf[CurClass].pinlist = (int *) my_malloc (NumClassPins * sizeof (int));
			for (k = 0; k < NumClassPins; ++k)
			{
				Type->class_inf[CurClass].pinlist[k] = CurPin;
				++CurPin;
			}

			/* Figure out class type */
			Prop = FindProperty (Cur, "type", TRUE);
			if (0 == strcmp (Prop, "in"))
			{
				Type->num_receivers += NumClassPins;
				Type->class_inf[CurClass].type = RECEIVER;
			}

			else if (0 == strcmp (Prop, "out"))
			{
				Type->num_drivers += NumClassPins;
				Type->class_inf[CurClass].type = DRIVER;
			}

			else if (0 == strcmp (Prop, "global"))
			{
				Type->class_inf[CurClass].type = RECEIVER;
			}

			else
			{
				printf (ERRTAG "Invalid pin class type '%s'.\n", Prop);
				exit (1);
			}
			++CurClass;
			Cur = Cur->next;
		}
	}
}


/* Allocs structures that describe pins and reads pin names. Sets
 * pin/class mappings. Unliinks the class descriptor nodes from XML.
 * For capacity > 1 pins and pinclasses are replicated because while
 * they share the same properties and setup, they are treated as
 * being unique. */
static void
SetupPinClasses (
    ezxml_t Classes,
    t_type_descriptor * Type)
{
	int CurClass, i, j, CurPin, PinsPerSubtile, ClassesPerSubtile;

	ezxml_t Cur, Prev;
	const char *Prop;

	boolean IsGlobal;
	char **Tokens;
	int *pin_used = NULL;


	/* Allocs and sets up the classinf data and counts pins */
	SetupClassInf (Classes, Type);
	PinsPerSubtile = Type->num_pins / Type->capacity;
	ClassesPerSubtile = Type->num_class / Type->capacity;

	/* Alloc num_pin sized lists. Replicated pins don't have new names. */
	Type->is_global_pin = (boolean *) my_malloc (Type->num_pins * sizeof (boolean));
	Type->pin_class = (int *) my_malloc (Type->num_pins * sizeof (int));
	pin_used = (int *) my_malloc (Type->num_pins * sizeof (int));
	for (i = 0; i < Type->num_pins; ++i)
	{
		pin_used[i] = FALSE;
	}

	/* Iterate over all pins */
	CurClass = 0;
	Cur = Classes->child;
	while (Cur)
	{
		CheckElement (Cur, "class");

		/* Figure out if global class */
		IsGlobal = FALSE;
		Prop = FindProperty (Cur, "type", TRUE);
		if (0 == strcmp (Prop, "global"))
		{
			IsGlobal = TRUE;
		}
		ezxml_set_attr (Cur, "type", NULL);

		/* Itterate over pins in the class */
		Tokens = GetNodeTokens (Cur);
		assert (CountTokens (Tokens) == Type->class_inf[CurClass].num_pins);
		for (j = 0; j < Type->class_inf[CurClass].num_pins; ++j)
		{
			CurPin = my_atoi (Tokens[j]);

			/* Check for duplicate pin names */
			if (pin_used[CurPin])
			{
				printf (ERRTAG "Pin %d is defined in two different classes in type '%s'.\n", CurPin, Type->name);
				exit (1);
			}
			pin_used[CurPin] = TRUE;

			/* Set the pin class and is_global status */
			for (i = 0; i < Type->capacity; ++i)
			{
				Type->pin_class[CurPin + (i * PinsPerSubtile)] = CurClass + (i * ClassesPerSubtile);
				Type->is_global_pin[CurPin + (i * PinsPerSubtile)] = IsGlobal;
			}
		}
		FreeTokens (&Tokens);
		++CurClass;
		Prev = Cur;
		Cur = Cur->next;
		FreeNode (Prev);
	}
	if (pin_used)
	{
		free (pin_used);
		pin_used = NULL;
	}
}


/* Sets up the pinloc map for the type. Unlinks the loc nodes
 * from the XML tree.
 * Pins and pin classses must already be setup by SetupPinClasses */
static void
SetupPinLocations (
    ezxml_t Locations,
    t_type_descriptor * Type)
{
	int i, j, k, l, PinsPerSubtile, Count, Len;

	ezxml_t Cur, Prev;
	const char *Prop;
	char **Tokens, **CurTokens;

	PinsPerSubtile = Type->num_pins / Type->capacity;

	/* Alloc and clear pin locations */
	Type->pinloc = (int ***) my_malloc (Type->height * sizeof (int **));
	for (i = 0; i < Type->height; ++i)
	{
		Type->pinloc[i] = (int **) my_malloc (4 * sizeof (int *));
		for (j = 0; j < 4; ++j)
		{
			Type->pinloc[i][j] = (int *) my_malloc (Type->num_pins * sizeof (int));
			for (k = 0; k < Type->num_pins; ++k)
			{
				Type->pinloc[i][j][k] = 0;
			}
		}
	}

	/* Load the pin locations */
	Cur = Locations->child;
	while (Cur)
	{
		CheckElement (Cur, "loc");

		/* Get offset */
		i = 0;
		Prop = FindProperty (Cur, "offset", FALSE);
		if (Prop)
		{
			i = my_atoi (Prop);
			if ((i < 0) || (i >= Type->height))
			{
				printf (ERRTAG "%d is an invalid offset for type '%s'.\n", i, Type->name);
				exit (1);
			}
			ezxml_set_attr (Cur, "offset", NULL);
		}

		/* Get side */
		Prop = FindProperty (Cur, "side", TRUE);
		if (0 == strcmp (Prop, "left"))
		{
			j = LEFT;
		}

		else if (0 == strcmp (Prop, "top"))
		{
			j = TOP;
		}

		else if (0 == strcmp (Prop, "right"))
		{
			j = RIGHT;
		}

		else if (0 == strcmp (Prop, "bottom"))
		{
			j = BOTTOM;
		}

		else
		{
			printf (ERRTAG "'%s' is not a valid side.\n", Prop);
			exit (1);
		}
		ezxml_set_attr (Cur, "side", NULL);

		/* Check location is on perimeter */
		if ((TOP == j) && (i != (Type->height - 1)))
		{
			printf (ERRTAG "Locations are only allowed on large block " "perimeter. 'top' side should be at offset %d only.\n", (Type->height - 1));
			exit (1);
		}
		if ((BOTTOM == j) && (i != 0))
		{
			printf (ERRTAG "Locations are only allowed on large block " "perimeter. 'bottom' side should be at offset 0 only.\n");
			exit (1);
		}

		/* Go through lists of pins */
		CountTokensInString (Cur->txt, &Count, &Len);
		if (Count > 0)
		{
			Tokens = GetNodeTokens (Cur);
			CurTokens = Tokens;
			while (*CurTokens)
			{

				/* Get pin */
				k = my_atoi (*CurTokens);
				if (k >= Type->num_pins)
				{
					printf (ERRTAG "Pin %d of type '%s' is not a valid pin.\n", k, Type->name);
					exit (1);
				}

				/* Set pin location */
				for (l = 0; l < Type->capacity; ++l)
				{
					Type->pinloc[i][j][k + (l * PinsPerSubtile)] = 1;
				}

				/* Advance through list of pins in this location */
				++CurTokens;
			}
			FreeTokens (&Tokens);
		}
		Prev = Cur;
		Cur = Cur->next;
		FreeNode (Prev);
	}
}


/* Sets up the grid_loc_def for the type. Unlinks the loc nodes
 * from the XML tree. */
t_type_ptr FILL_TYPE = NULL;
static void
SetupGridLocations (
    ezxml_t Locations,
    t_type_descriptor * Type)
{
	int i;

	ezxml_t Cur, Prev;
	const char *Prop;

	Type->num_grid_loc_def = CountChildren (Locations, "loc");
	Type->grid_loc_def = (struct s_grid_loc_def *) my_calloc (Type->num_grid_loc_def, sizeof (struct s_grid_loc_def));

	/* Load the pin locations */
	Cur = Locations->child;
	i = 0;
	while (Cur)
	{
		CheckElement (Cur, "loc");

		/* loc index */
		Prop = FindProperty (Cur, "type", TRUE);
		if (Prop)
		{
			if (strcmp (Prop, "fill") == 0)
			{
				if (Type->num_grid_loc_def != 1)
				{
					printf (ERRTAG "Another loc specified for fill.\n");
					exit (1);
				}
				Type->grid_loc_def[i].grid_loc_type = FILL;
				FILL_TYPE = Type;
			}
			else if (strcmp (Prop, "col") == 0)
			{
				Type->grid_loc_def[i].grid_loc_type = COL_REPEAT;
			}
			else if (strcmp (Prop, "rel") == 0)
			{
				Type->grid_loc_def[i].grid_loc_type = COL_REL;
			}
			else
			{
				printf (ERRTAG "Unknown grid location type '%s' for type '%s'.\n", Prop, Type->name);
				exit (1);
			}
			ezxml_set_attr (Cur, "type", NULL);
		}
		Prop = FindProperty (Cur, "start", FALSE);
		if (Type->grid_loc_def[i].grid_loc_type == COL_REPEAT)
		{
			if (Prop == NULL)
			{
				printf (ERRTAG "grid location property 'start' must be specified for grid location type 'col'.\n");
				exit (1);
			}
			Type->grid_loc_def[i].start_col = my_atoi (Prop);
			ezxml_set_attr (Cur, "start", NULL);
		}
		else if (Prop != NULL)
		{
			printf (ERRTAG "grid location property 'start' valid for grid location type 'col' only.\n");
			exit (1);
		}
		Prop = FindProperty (Cur, "repeat", FALSE);
		if (Type->grid_loc_def[i].grid_loc_type == COL_REPEAT)
		{
			if (Prop != NULL)
			{
				Type->grid_loc_def[i].repeat = my_atoi (Prop);
				ezxml_set_attr (Cur, "repeat", NULL);
			}
		}
		else if (Prop != NULL)
		{
			printf (ERRTAG "grid location property 'repeat' valid for grid location type 'col' only.\n");
			exit (1);
		}
		Prop = FindProperty (Cur, "pos", FALSE);
		if (Type->grid_loc_def[i].grid_loc_type == COL_REL)
		{
			if (Prop == NULL)
			{
				printf (ERRTAG "grid location property 'pos' must be specified for grid location type 'rel'.\n");
				exit (1);
			}
			Type->grid_loc_def[i].col_rel = atof (Prop);
			ezxml_set_attr (Cur, "pos", NULL);
		}
		else if (Prop != NULL)
		{
			printf (ERRTAG "grid location property 'pos' valid for grid location type 'rel' only.\n");
			exit (1);
		}
		Prop = FindProperty (Cur, "priority", FALSE);
		if (Prop)
		{
			Type->grid_loc_def[i].priority = my_atoi (Prop);
			ezxml_set_attr (Cur, "priority", NULL);
		}
		Prev = Cur;
		Cur = Cur->next;
		FreeNode (Prev);
		i++;
	}
}
static void
SetupTypeTiming (
    ezxml_t timing,
    t_type_descriptor * Type)
{
	ezxml_t Cur, Prev;
	const char *Prop;
	float value;
	char **Tokens;


	/* Clear timing info for type */
	Type->type_timing_inf.T_fb_ipin_to_sblk_ipin = 0;
	Type->type_timing_inf.T_sblk_opin_to_fb_opin = 0;
	Type->type_timing_inf.T_sblk_opin_to_sblk_ipin = 0;

	/* Load the timing info */
	Cur = timing->child;
	while (Cur)
	{
		CheckElement (Cur, "tedge");

		/* Get timing type */
		Prop = FindProperty (Cur, "type", TRUE);
		Tokens = GetNodeTokens (Cur);
		if (CountTokens (Tokens) != 1)
		{
			printf (ERRTAG "Timing for sequential subblock edges not a number.");
		}
		value = atof (Tokens[0]);
		if (0 == strcmp (Prop, "T_fb_ipin_to_sblk_ipin"))
		{
			Type->type_timing_inf.T_fb_ipin_to_sblk_ipin = value;
		}
		else if (0 == strcmp (Prop, "T_sblk_opin_to_fb_opin"))
		{
			Type->type_timing_inf.T_sblk_opin_to_fb_opin = value;
		}
		else if (0 == strcmp (Prop, "T_sblk_opin_to_sblk_ipin"))
		{
			Type->type_timing_inf.T_sblk_opin_to_sblk_ipin = value;
		}
		else
		{
			printf (ERRTAG "'%s' is an unrecognized timing edge.\n", Prop);
			exit (1);
		}
		ezxml_set_attr (Cur, "type", NULL);
		Prev = Cur;
		Cur = Cur->next;
		FreeNode (Prev);
		FreeTokens (&Tokens);
	}
}
static void
SetupSubblocksTcomb (
    ezxml_t timing,
    t_type_descriptor * Type)
{
	ezxml_t Cur, prev;
	t_T_subblock **T_subblock_ptr;
	float **t_comb;
	int i, j;
	char **Tokens;

	T_subblock_ptr = &Type->type_timing_inf.T_subblock;
	t_comb = (float **) alloc_matrix (0, Type->max_subblock_inputs - 1, 0, Type->max_subblock_outputs - 1, sizeof (float));
	for (i = 0; i < Type->max_subblock_inputs; i++)
	{
		for (j = 0; j < Type->max_subblock_outputs; j++)
		{
			t_comb[i][j] = 0;
		}
	}

	/* Load the timing info */
	Cur = timing->child;
	i = 0;
	while (Cur)
	{
		CheckElement (Cur, "trow");
		Tokens = GetNodeTokens (Cur);
		if (CountTokens (Tokens) != Type->max_subblock_outputs)
		{
			printf (ERRTAG "Number of tokens %d not equal number of subblock outputs %d.", CountTokens (Tokens), Type->max_subblock_outputs);
			exit (1);
		}
		for (j = 0; j < Type->max_subblock_outputs; j++)
		{
			t_comb[i][j] = atof (Tokens[j]);
		}
		i++;
		prev = Cur;
		Cur = Cur->next;
		FreeNode (prev);
		FreeTokens (&Tokens);
	}
	if (i != Type->max_subblock_inputs)
	{
		printf (ERRTAG "Number of trow %d not equal number of subblock inputs %d.", i, Type->max_subblock_inputs);
		exit (1);
	}
	for (i = 0; i < Type->max_subblocks; i++)
	{
		(*T_subblock_ptr)[i].T_comb = t_comb;
	}
}
static void
SetupSubblocksTSeq (
    ezxml_t timing_seq_in,
    ezxml_t timing_seq_out,
    t_type_descriptor * Type)
{
	ezxml_t Cur, prev;
	t_T_subblock **T_subblock_ptr;
	int i, j;
	float *t_seq[2];

	ezxml_t seq_timing[2];
	char **Tokens;

	T_subblock_ptr = &Type->type_timing_inf.T_subblock;
	seq_timing[0] = timing_seq_in;
	seq_timing[1] = timing_seq_out;
	t_seq[0] = (float *) my_calloc (Type->max_subblock_inputs, sizeof (float));
	t_seq[1] = (float *) my_calloc (Type->max_subblock_outputs, sizeof (float));

	/* Load the timing info */
	for (i = 0; i < 2; i++)
	{
		Cur = seq_timing[i]->child;
		j = 0;
		while (Cur)
		{
			CheckElement (Cur, "trow");
			Tokens = GetNodeTokens (Cur);
			if (CountTokens (Tokens) != 1)
			{
				printf (ERRTAG "Timing for sequential subblock edges not a number.");
			}
			t_seq[i][j] = atof (Tokens[0]);
			j++;
			prev = Cur;
			Cur = Cur->next;
			FreeNode (prev);
			FreeTokens (&Tokens);
		}
		if (j != Type->max_subblock_outputs)
		{
			printf (ERRTAG "Number of trow %d not equal number of subblock outputs %d.", j, Type->max_subblock_outputs);
			exit (1);
		}
	}
	for (i = 0; i < Type->max_subblocks; i++)
	{
		(*T_subblock_ptr)[i].T_seq_in = t_seq[0];
		(*T_subblock_ptr)[i].T_seq_out = t_seq[1];
	}
}


/* Takes in the node ptr for the 'fc_in' and 'fc_out' elements and initializes
 * the appropriate fields of type. Unlinks the contents of the nodes. */
static void
Process_Fc (
    ezxml_t Fc_in_node,
    ezxml_t Fc_out_node,
    t_type_descriptor * Type)
{
	enum Fc_type Type_in;
	enum Fc_type Type_out;

	ParseFc (Fc_in_node, &Type_in, &Type->Fc_in);
	ParseFc (Fc_out_node, &Type_out, &Type->Fc_out);
	if (FC_FULL == Type_in)
	{
		printf (ERRTAG "'full' Fc type isn't allowed for Fc_in.\n");
		exit (1);
	}
	Type->is_Fc_out_full_flex = FALSE;
	Type->is_Fc_frac = FALSE;
	if (FC_FULL == Type_out)
	{
		Type->is_Fc_out_full_flex = TRUE;
	}

	else if (Type_in != Type_out)
	{
		printf (ERRTAG "Fc_in and Fc_out must have same type unless Fc_out has type 'full'.\n");
		exit (1);
	}
	if (FC_FRAC == Type_in)
	{
		Type->is_Fc_frac = TRUE;
	}
}


/* This parses contents of the 'subblocks' element and unlinks from tree */
static void
ProcessSubblocks (
    INOUTP ezxml_t Parent,
    INOUTP t_type_descriptor * Type,
    boolean timing_enabled)
{
	const char *Prop;

	ezxml_t CurType, Cur, Cur2;
	Type->max_subblocks = 1;
	Prop = FindProperty (Parent, "max_subblocks", FALSE);
	if (Prop)
	{
		Type->max_subblocks = my_atoi (Prop);
		ezxml_set_attr (Parent, "max_subblocks", NULL);
	}
	Type->max_subblock_inputs = 1;
	Prop = FindProperty (Parent, "max_subblock_inputs", FALSE);
	if (Prop)
	{
		Type->max_subblock_inputs = my_atoi (Prop);
		ezxml_set_attr (Parent, "max_subblock_inputs", NULL);
	}
	Type->max_subblock_outputs = 1;
	Prop = FindProperty (Parent, "max_subblock_outputs", FALSE);
	if (Prop)
	{
		Type->max_subblock_outputs = my_atoi (Prop);
		ezxml_set_attr (Parent, "max_subblock_outputs", NULL);
	}
	CurType = FindElement (Parent, "timing", timing_enabled);
	if (CurType != NULL)
	{
		Type->type_timing_inf.T_subblock = (t_T_subblock *) my_malloc (Type->max_subblocks * sizeof (t_T_subblock));

		/* Load T_comb timing for subblock */
		Cur = FindElement (CurType, "T_comb", TRUE);
		SetupSubblocksTcomb (Cur, Type);
		FreeNode (Cur);

		/* Load Fc */
		Cur = FindElement (CurType, "T_seq_in", TRUE);
		Cur2 = FindElement (CurType, "T_seq_out", TRUE);
		SetupSubblocksTSeq (Cur, Cur2, Type);
		FreeNode (Cur);
		FreeNode (Cur2);
		FreeNode (CurType);
	}
}


/* Thie processes attributes of the 'type' tag and then unlinks them */
static void
ProcessTypeProps (
    ezxml_t Node,
    t_type_descriptor * Type)
{
	const char *Prop;


	/* Load type name */
	Prop = FindProperty (Node, "name", TRUE);
	Type->name = my_strdup (Prop);
	ezxml_set_attr (Node, "name", NULL);

	/* Load capacity */
	Type->capacity = 1;		/* DEFAULT */

	/* Load height */
	Type->height = 1;		/* DEFAULT */
	Prop = FindProperty (Node, "height", FALSE);
	if (Prop)
	{
		Type->height = my_atoi (Prop);
		ezxml_set_attr (Node, "height", NULL);
	}
}


/* Takes in node pointing to <layout> and loads all the
 * child type objects. Unlinks the entire <layout> node
 * when complete. */
static void
ProcessLayout (
    INOUTP ezxml_t Node,
    OUTP struct s_arch *arch)
{
	const char *Prop;

	arch->clb_grid.IsAuto = TRUE;

	/* Load width and height if applicable */
	Prop = FindProperty (Node, "width", FALSE);
	if (Prop != NULL)
	{
		arch->clb_grid.IsAuto = FALSE;
		arch->clb_grid.W = my_atoi (Prop);
		ezxml_set_attr (Node, "width", NULL);
		Prop = FindProperty (Node, "height", TRUE);
		if (Prop != NULL)
		{
			arch->clb_grid.H = my_atoi (Prop);
			ezxml_set_attr (Node, "height", NULL);
		}
	}

	/* Load aspect ratio if applicable */
	Prop = FindProperty (Node, "auto", arch->clb_grid.IsAuto);
	if (Prop != NULL)
	{
		if (arch->clb_grid.IsAuto == FALSE)
		{
			printf (ERRTAG "Auto-sizing, width and height cannot be specified\n");
		}
		arch->clb_grid.Aspect = atof (Prop);
		ezxml_set_attr (Node, "auto", NULL);
	}
}


/* Takes in node pointing to <device> and loads all the
 * child type objects. Unlinks the entire <device> node
 * when complete. */
static void
ProcessDevice (
    INOUTP ezxml_t Node,
    OUTP struct s_arch *arch,
    INP boolean timing_enabled)
{
	const char *Prop;

	ezxml_t Cur;
	Cur = FindElement (Node, "sizing", TRUE);
	arch->R_minW_nmos = 0;
	Prop = FindProperty (Cur, "R_minW_nmos", timing_enabled);
	if (Prop != NULL)
	{
		arch->R_minW_nmos = atof (Prop);
		ezxml_set_attr (Cur, "R_minW_nmos", NULL);
	}
	arch->R_minW_pmos = 0;
	Prop = FindProperty (Cur, "R_minW_pmos", timing_enabled);
	if (Prop != NULL)
	{
		arch->R_minW_pmos = atof (Prop);
		ezxml_set_attr (Cur, "R_minW_pmos", NULL);
	}
	arch->ipin_mux_trans_size = 0;
	Prop = FindProperty (Cur, "ipin_mux_trans_size", FALSE);
	if (Prop != NULL)
	{
		arch->ipin_mux_trans_size = atof (Prop);
		ezxml_set_attr (Cur, "ipin_mux_trans_size", NULL);
	}
	FreeNode (Cur);
	Cur = FindElement (Node, "timing", timing_enabled);
	if (Cur != NULL)
	{
		arch->C_ipin_cblock = 0;
		Prop = FindProperty (Cur, "C_ipin_cblock", FALSE);
		if (Prop != NULL)
		{
			arch->C_ipin_cblock = atof (Prop);
			ezxml_set_attr (Cur, "C_ipin_cblock", NULL);
		}
		arch->T_ipin_cblock = 0;
		Prop = FindProperty (Cur, "T_ipin_cblock", FALSE);
		if (Prop != NULL)
		{
			arch->T_ipin_cblock = atof (Prop);
			ezxml_set_attr (Cur, "T_ipin_cblock", NULL);
		}
		FreeNode (Cur);
	}
	Cur = FindElement (Node, "area", TRUE);
	Prop = FindProperty (Cur, "grid_logic_tile_area", FALSE);
	arch->grid_logic_tile_area = 0;
	if (Prop != NULL)
	{
		arch->grid_logic_tile_area = atof (Prop);
		ezxml_set_attr (Cur, "grid_logic_tile_area", NULL);
	}
	FreeNode (Cur);
	Cur = FindElement (Node, "chan_width_distr", FALSE);
	if (Cur != NULL)
	{
		ProcessChanWidthDistr (Cur, arch);
		FreeNode (Cur);
	}
	Cur = FindElement (Node, "switch_block", TRUE);
	Prop = FindProperty (Cur, "type", TRUE);
	if (strcmp (Prop, "wilton") == 0)
	{
		arch->SBType = WILTON;
	}
	else if (strcmp (Prop, "universal") == 0)
	{
		arch->SBType = UNIVERSAL;
	}
	else if (strcmp (Prop, "subset") == 0)
	{
		arch->SBType = SUBSET;
	}
	else
	{
		printf (ERRTAG "Unknown property %s for switch block type x\n", Prop);
		exit (1);
	}
	ezxml_set_attr (Cur, "type", NULL);
	Prop = FindProperty (Cur, "fs", TRUE);
	arch->Fs = my_atoi (Prop);
	ezxml_set_attr (Cur, "fs", NULL);
	FreeNode (Cur);
}


/* Takes in node pointing to <chan_width_distr> and loads all the
 * child type objects. Unlinks the entire <chan_width_distr> node
 * when complete. */
static void
ProcessChanWidthDistr (
    INOUTP ezxml_t Node,
    OUTP struct s_arch *arch)
{
	const char *Prop;

	ezxml_t Cur;
	Cur = FindElement (Node, "io", TRUE);
	Prop = FindProperty (Cur, "width", TRUE);
	arch->Chans.chan_width_io = atof (Prop);
	ezxml_set_attr (Cur, "width", NULL);
	FreeNode (Cur);
	Cur = FindElement (Node, "x", TRUE);
	ProcessChanWidthDistrDir (Cur, &arch->Chans.chan_x_dist);
	FreeNode (Cur);
	Cur = FindElement (Node, "y", TRUE);
	ProcessChanWidthDistrDir (Cur, &arch->Chans.chan_y_dist);
	FreeNode (Cur);
}


/* Takes in node within <chan_width_distr> and loads all the
 * child type objects. Unlinks the entire node when complete. */
static void
ProcessChanWidthDistrDir (
    INOUTP ezxml_t Node,
    OUTP t_chan * chan)
{
	const char *Prop;

	boolean hasXpeak, hasWidth, hasDc;
	hasXpeak = hasWidth = hasDc = FALSE;
	Prop = FindProperty (Node, "distr", TRUE);
	if (strcmp (Prop, "uniform") == 0)
	{
		chan->type = UNIFORM;
	}
	else if (strcmp (Prop, "gaussian") == 0)
	{
		chan->type = GAUSSIAN;
		hasXpeak = hasWidth = hasDc = TRUE;
	}
	else if (strcmp (Prop, "pulse") == 0)
	{
		chan->type = PULSE;
		hasXpeak = hasWidth = hasDc = TRUE;
	}
	else if (strcmp (Prop, "delta") == 0)
	{
		hasXpeak = hasDc = TRUE;
		chan->type = DELTA;
	}
	else
	{
		printf (ERRTAG "Unknown property %s for chan_width_distr x\n", Prop);
		exit (1);
	}
	ezxml_set_attr (Node, "distr", NULL);
	Prop = FindProperty (Node, "peak", TRUE);
	chan->peak = atof (Prop);
	ezxml_set_attr (Node, "peak", NULL);
	Prop = FindProperty (Node, "width", hasWidth);
	if (hasWidth)
	{
		chan->width = atof (Prop);
		ezxml_set_attr (Node, "width", NULL);
	}
	Prop = FindProperty (Node, "xpeak", hasXpeak);
	if (hasXpeak)
	{
		chan->xpeak = atof (Prop);
		ezxml_set_attr (Node, "xpeak", NULL);
	}
	Prop = FindProperty (Node, "dc", hasDc);
	if (hasDc)
	{
		chan->dc = atof (Prop);
		ezxml_set_attr (Node, "dc", NULL);
	}
}
static void
SetupEmptyType (
)
{
	t_type_descriptor *type;
	type = &type_descriptors[EMPTY_TYPE->index];
	type->name = "<EMPTY>";
	type->num_pins = 0;
	type->height = 1;
	type->capacity = 0;
	type->num_drivers = 0;
	type->num_receivers = 0;
	type->pinloc = NULL;
	type->num_class = 0;
	type->class_inf = NULL;
	type->pin_class = NULL;
	type->is_global_pin = NULL;
	type->is_Fc_frac = TRUE;
	type->is_Fc_out_full_flex = FALSE;
	type->Fc_in = 0;
	type->Fc_out = 0;
	type->max_subblocks = 0;
	type->max_subblock_inputs = 0;
	type->max_subblock_outputs = 0;

	/* Used as lost area filler, no definition */
	type->grid_loc_def = NULL;
	type->num_grid_loc_def = 0;
}


/* Takes in node pointing to <io> and loads all the
 * child type objects. Unlinks the entire <io> node
 * when complete. */
static void
ProcessIO (
    INOUTP ezxml_t Node,
    INP boolean timing_enabled)
{
	const char *Prop;

	ezxml_t Cur, Cur2;
	int i, j;

	t_type_descriptor *type;
	int num_inputs, num_outputs, num_clocks, num_pins, capacity, t_in, t_out;
	enum
	{ INCLASS = 0, OUTCLASS = 1, CLKCLASS = 2 };

	type = &type_descriptors[IO_TYPE->index];
	num_inputs = 1;
	num_outputs = 1;
	num_clocks = 1;
	CheckElement (Node, "io");
	type->name = ".io";
	type->height = 1;

	/* Load capacity */
	Prop = FindProperty (Node, "capacity", TRUE);
	capacity = my_atoi (Prop);
	type->capacity = capacity;
	ezxml_set_attr (Node, "capacity", NULL);

	/* Load Fc */
	Cur = FindElement (Node, "fc_in", TRUE);
	Cur2 = FindElement (Node, "fc_out", TRUE);
	Process_Fc (Cur, Cur2, type);
	FreeNode (Cur);
	FreeNode (Cur2);

	/* Initialize and setup type */
	num_pins = 3 * capacity;
	type->num_pins = num_pins;
	type->num_drivers = num_outputs * capacity;
	type->num_receivers = num_inputs * capacity;
	type->pinloc = (int ***) alloc_matrix3 (0, 0, 0, 3, 0, num_pins - 1, sizeof (int));

	/* Jason Luu - September 5, 2007
	 * To treat IOs as any other block in routing, need to blackbox
	 * as having physical pins on all sides.  This is a hack. */
	for (i = 0; i < num_pins; ++i)
	{
		for (j = 0; j < 4; ++j)
		{
			type->pinloc[0][j][i] = 1;
		}
	}

	/* Three fixed classes.  In, Out, Clock */
	type->num_class = 3 * capacity;
	type->class_inf = (struct s_class *) my_malloc (sizeof (struct s_class) * 3 * capacity);
	type->pin_class = (int *) my_malloc (sizeof (int) * num_pins);
	type->is_global_pin = (boolean *) my_malloc (sizeof (boolean) * num_pins);
	for (j = 0; j < capacity; ++j)
	{

		/* Three fixed classes. In, Out, Clock */
		type->class_inf[3 * j + INCLASS].num_pins = num_inputs;
		type->class_inf[3 * j + INCLASS].type = RECEIVER;
		type->class_inf[3 * j + INCLASS].pinlist = (int *) my_malloc (sizeof (int) * num_inputs);
		for (i = 0; i < num_inputs; ++i)
		{
			type->class_inf[3 * j + INCLASS].pinlist[i] = num_pins * j / capacity + i;
			type->pin_class[num_pins * j / capacity + i] = 3 * j + INCLASS;
			type->is_global_pin[num_pins * j / capacity + i] = FALSE;
		}
		type->class_inf[3 * j + OUTCLASS].num_pins = num_outputs;
		type->class_inf[3 * j + OUTCLASS].type = DRIVER;
		type->class_inf[3 * j + OUTCLASS].pinlist = (int *) my_malloc (sizeof (int) * num_outputs);
		for (i = 0; i < num_outputs; ++i)
		{
			type->class_inf[3 * j + OUTCLASS].pinlist[i] = num_pins * j / capacity + i + num_inputs;
			type->pin_class[num_pins * j / capacity + i + num_inputs] = 3 * j + OUTCLASS;
			type->is_global_pin[num_pins * j / capacity + i + num_inputs] = FALSE;
		}
		type->class_inf[3 * j + CLKCLASS].num_pins = num_clocks;
		type->class_inf[3 * j + CLKCLASS].type = RECEIVER;
		type->class_inf[3 * j + CLKCLASS].pinlist = (int *) my_malloc (sizeof (int) * num_clocks);
		for (i = 0; i < num_clocks; ++i)
		{
			type->class_inf[3 * j + CLKCLASS].pinlist[i] = num_pins * j / capacity + i + num_inputs + num_outputs;
			type->pin_class[num_pins * j / capacity + i + num_inputs + num_outputs] = 3 * j + CLKCLASS;
			type->is_global_pin[num_pins * j / capacity + i + num_inputs + num_outputs] = TRUE;
		}
	}
	type->max_subblocks = 1;
	type->max_subblock_inputs = 1;
	type->max_subblock_outputs = 1;

	/* Always at boundary */
	type->num_grid_loc_def = 1;
	type->grid_loc_def = (struct s_grid_loc_def *) my_calloc (1, sizeof (struct s_grid_loc_def));
	type->grid_loc_def[0].grid_loc_type = BOUNDARY;
	type->grid_loc_def[0].priority = 0;
	t_in = -1;
	t_out = -1;
	Prop = FindProperty (Node, "t_inpad", timing_enabled);
	if (Prop != NULL)
	{
		t_in = atof (Prop);
		ezxml_set_attr (Node, "t_inpad", NULL);
	}
	Prop = FindProperty (Node, "t_outpad", timing_enabled);
	if (Prop != NULL)
	{
		t_out = atof (Prop);
		ezxml_set_attr (Node, "t_outpad", NULL);
	}
	if (timing_enabled)
	{
		type->type_timing_inf.T_fb_ipin_to_sblk_ipin = 0;
		type->type_timing_inf.T_sblk_opin_to_fb_opin = 0;
		type->type_timing_inf.T_sblk_opin_to_sblk_ipin = 0;
		type->type_timing_inf.T_subblock = (t_T_subblock *) my_malloc (sizeof (t_T_subblock));
		type->type_timing_inf.T_subblock[0].T_comb = (float **) my_malloc (sizeof (float *));
		type->type_timing_inf.T_subblock[0].T_comb[0] = (float *) my_malloc (sizeof (float));
		type->type_timing_inf.T_subblock[0].T_comb[0][0] = t_out;
		type->type_timing_inf.T_subblock[0].T_seq_in = (float *) my_malloc (sizeof (float));
		type->type_timing_inf.T_subblock[0].T_seq_in[0] = 0;
		type->type_timing_inf.T_subblock[0].T_seq_out = (float *) my_malloc (sizeof (float));
		type->type_timing_inf.T_subblock[0].T_seq_out[0] = t_in;
	}
}


/* Takes in node pointing to <typelist> and loads all the
 * child type objects. Unlinks the entire <typelist> node
 * when complete. */
static void
ProcessTypes (
    INOUTP ezxml_t Node,
    OUTP t_type_descriptor ** Types,
    OUTP int *NumTypes,
    boolean timing_enabled)
{
	ezxml_t CurType, Prev;
	ezxml_t Cur, Cur2;
	t_type_descriptor *Type;
	int i;


	/* Alloc the type list. Need two additional t_type_desctiptors:
	 * 1: empty psuedo-type
	 * 2: IO type
	 */
	*NumTypes = CountChildren (Node, "type") + 2;
	*Types = (t_type_descriptor *) my_malloc (sizeof (t_type_descriptor) * (*NumTypes));
	EMPTY_TYPE = &type_descriptors[EMPTY_TYPE_INDEX];
	IO_TYPE = &type_descriptors[IO_TYPE_INDEX];
	type_descriptors[EMPTY_TYPE_INDEX].index = EMPTY_TYPE_INDEX;
	type_descriptors[IO_TYPE_INDEX].index = IO_TYPE_INDEX;
	SetupEmptyType ();
	Cur = FindElement (Node, "io", TRUE);
	ProcessIO (Cur, timing_enabled);
	FreeNode (Cur);

	/* Process the types */
	i = 2;			/* Skip over 'empty' and 'io' type */
	CurType = Node->child;
	while (CurType)
	{
		CheckElement (CurType, "type");

		/* Alias to current type */
		Type = &(*Types)[i];

		/* Parses the properties fields of the type */
		ProcessTypeProps (CurType, Type);

		/* Load subblock info */
		Cur = FindElement (CurType, "subblocks", TRUE);
		ProcessSubblocks (Cur, Type, timing_enabled);
		FreeNode (Cur);

		/* Load Fc */
		Cur = FindElement (CurType, "fc_in", TRUE);
		Cur2 = FindElement (CurType, "fc_out", TRUE);
		Process_Fc (Cur, Cur2, Type);
		FreeNode (Cur);
		FreeNode (Cur2);

		/* Load pin names and classes and locations */
		Cur = FindElement (CurType, "pinclasses", TRUE);
		SetupPinClasses (Cur, Type);
		FreeNode (Cur);
		Cur = FindElement (CurType, "pinlocations", TRUE);
		SetupPinLocations (Cur, Type);
		FreeNode (Cur);
		Cur = FindElement (CurType, "gridlocations", TRUE);
		SetupGridLocations (Cur, Type);
		FreeNode (Cur);
		Cur = FindElement (CurType, "timing", timing_enabled);
		if (Cur)
		{
			SetupTypeTiming (Cur, Type);
			FreeNode (Cur);
		}
		Type->index = i;

		/* Type fully read */
		++i;

		/* Free this node and get its next sibling node */
		Prev = CurType;
		CurType = CurType->next;
		FreeNode (Prev);
	}
	if (FILL_TYPE == NULL)
	{
		printf (ERRTAG "grid location type 'fill' must be specified.\n");
		exit (1);
	}
}


/* Loads the given architecture file. Currently only
 * handles type information */
void
XmlReadArch (
    INP const char *ArchFile,
    INP boolean timing_enabled,
    OUTP struct s_arch *arch,
    OUTP t_type_descriptor ** Types,
    OUTP int *NumTypes,
    OUTP struct s_clocks *clocks,
    OUTP struct s_power *power)
{
	ezxml_t Cur, Next;

	/* Parse the file */
	Cur = ezxml_parse_file (ArchFile);
	if (NULL == Cur)
	{
		printf (ERRTAG "Unable to load architecture file '%s'.\n", ArchFile);
	}

	/* Root node should be architecture */
	CheckElement (Cur, "architecture");

	/* Process layout */
	Next = FindElement (Cur, "layout", TRUE);
	ProcessLayout (Next, arch);
	FreeNode (Next);

	/* Process device */
	Next = FindElement (Cur, "device", TRUE);
	ProcessDevice (Next, arch, timing_enabled);
	FreeNode (Next);

	/* Process types */
	Next = FindElement (Cur, "typelist", TRUE);
	ProcessTypes (Next, Types, NumTypes, timing_enabled);
	FreeNode (Next);

	/* Process switches */
	Next = FindElement (Cur, "switchlist", TRUE);
	ProcessSwitches (Next, &(arch->Switches), &(arch->num_switches), timing_enabled);
	FreeNode (Next);

	/* Process segments. This depends on switches */
	Next = FindElement (Cur, "segmentlist", TRUE);
	ProcessSegments (Next, &(arch->Segments), &(arch->num_segments), arch->Switches, arch->num_switches, timing_enabled);
	FreeNode (Next);

	/* Process power *//* PAJ */
	Next = FindElement (Cur, "power", FALSE);
	ProcessPower (Next, power);
	FreeNode (Next);

	/* Process Clocks *//* PAJ */
	Next = FindElement (Cur, "clocks", FALSE);
	ProcessClocks (Next, clocks);
	FreeNode (Next);

	/* Release the full XML tree */
	FreeNode (Cur);
}

static void
ProcessClocks (
    INOUTP ezxml_t Parent,
    OUTP t_clocks * clocks)
{
	ezxml_t Node;
	int i;
	const char *tmp;

	clocks->num_global_clock = CountChildren (Parent, "clock");

	/* Alloc the clockdetails */
	clocks->clock_inf = NULL;
	if (clocks->num_global_clock > 0)
	{
		clocks->clock_inf = (struct clock_details *) my_malloc (clocks->num_global_clock * sizeof (struct clock_details));
		memset (clocks->clock_inf, 0, clocks->num_global_clock * sizeof (struct clock_details));
	}

	/* Load the clock info. */
	for (i = 0; i < clocks->num_global_clock; ++i)
	{
		/* get the next clock item */
		Node = ezxml_child (Parent, "clock");

		tmp = FindProperty (Node, "buffer_R", FALSE);
		if (tmp)
		{
			clocks->clock_inf[i].buffer_R = atof (tmp);
		}
		ezxml_set_attr (Node, "buffer_R", NULL);

		tmp = FindProperty (Node, "buffer_Cin", FALSE);
		if (tmp)
		{
			clocks->clock_inf[i].buffer_Cin = atof (tmp);
		}
		ezxml_set_attr (Node, "buffer_Cin", NULL);

		tmp = FindProperty (Node, "buffer_Cout", FALSE);
		if (tmp)
		{
			clocks->clock_inf[i].buffer_Cout = atof (tmp);
		}
		ezxml_set_attr (Node, "buffer_Cout", NULL);

		tmp = FindProperty (Node, "Rwire", FALSE);
		if (tmp)
		{
			clocks->clock_inf[i].Rwire = atof (tmp);
		}
		ezxml_set_attr (Node, "Rwire", NULL);

		tmp = FindProperty (Node, "Cwire", FALSE);
		if (tmp)
		{
			clocks->clock_inf[i].Cwire = atof (tmp);
		}
		ezxml_set_attr (Node, "Cwire", NULL);

		tmp = FindProperty (Node, "Cin_per_clb_clock_pin", FALSE);
		if (tmp)
		{
			clocks->clock_inf[i].Cin_per_clb_clock_pin = atof (tmp);
		}
		ezxml_set_attr (Node, "Cin_per_clb_clock_pin", NULL);

		FreeNode (Node);
	}
}

static void
ProcessTempRecords (
    INOUTP ezxml_t Parent,
    OUTP t_power * power)
{
	ezxml_t Node;
	int i;
	const char *tmp;
	char **Tokens;

	power->num_temperature_records = CountChildren (Parent, "temp");

	/* Alloc the temperature record */
	power->NFS_records = NULL;
	if (power->num_temperature_records > 0)
	{
		power->NFS_records = (struct temperature_record *) my_malloc (power->num_temperature_records * sizeof (struct temperature_record));
		memset (power->NFS_records, 0, power->num_temperature_records * sizeof (struct temperature_record));
	}

	/* Load the temp record */
	for (i = 0; i < power->num_temperature_records; ++i)
	{
		/* get the next temp record item */
		Node = ezxml_child (Parent, "temp");

		tmp = FindProperty (Node, "NMOS_NFS", TRUE);
		if (tmp)
		{
			power->NFS_records[i].NMOS_NFS_value = atof (tmp);
		}
		ezxml_set_attr (Node, "NMOS_NFS", NULL);

		tmp = FindProperty (Node, "PMOS_NFS", TRUE);
		if (tmp)
		{
			power->NFS_records[i].PMOS_NFS_value = atof (tmp);
		}
		ezxml_set_attr (Node, "PMOS_NFS", NULL);

		Tokens = GetNodeTokens (Node);
		assert (CountTokens (Tokens) == 1);
		power->NFS_records[i].temperature = atof (Tokens[0]);
		FreeTokens (&Tokens);

		FreeNode (Node);
	}
}

static void
ProcessPower (
    INOUTP ezxml_t Parent,
    OUTP t_power * power)
{
	ezxml_t Cur;
	const char *Prop;
	char **Tokens;


	Cur = FindElement (Parent, "temperature_records", TRUE);
	ProcessTempRecords (Cur, power);
	FreeNode (Cur);

	Cur = FindElement (Parent, "CLB_Cwire", TRUE);
	Tokens = GetNodeTokens (Cur);
	assert (CountTokens (Tokens) == 1);
	power->clb_Cwire = atof(Tokens[0]);
	FreeTokens (&Tokens);
	FreeNode (Cur);

	Cur = FindElement (Parent, "Nmos", TRUE);
	Prop = FindProperty (Cur, "Vth", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.Vth = atof (Prop);
		ezxml_set_attr (Cur, "Vth", NULL);
	}
	Prop = FindProperty (Cur, "CJ", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.CJ = atof (Prop);
		ezxml_set_attr (Cur, "CJ", NULL);
	}
	Prop = FindProperty (Cur, "CJSW", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.CJSW = atof (Prop);
		ezxml_set_attr (Cur, "CJSW", NULL);
	}
	Prop = FindProperty (Cur, "CJSWG", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.CJSWG = atof (Prop);
		ezxml_set_attr (Cur, "CJSWG", NULL);
	}
	Prop = FindProperty (Cur, "CGDO", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.CGDO = atof (Prop);
		ezxml_set_attr (Cur, "CGDO", NULL);
	}
	Prop = FindProperty (Cur, "COX", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.COX = atof (Prop);
		ezxml_set_attr (Cur, "COX", NULL);
	}
	Prop = FindProperty (Cur, "EC", TRUE);
	if (Prop != NULL)
	{
		power->NMOS_tx_record.EC = atof (Prop);
		ezxml_set_attr (Cur, "EC", NULL);
	}
	FreeNode (Cur);

	Cur = FindElement (Parent, "Pmos", TRUE);
	Prop = FindProperty (Cur, "Vth", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.Vth = atof (Prop);
		ezxml_set_attr (Cur, "Vth", NULL);
	}
	Prop = FindProperty (Cur, "CJ", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.CJ = atof (Prop);
		ezxml_set_attr (Cur, "CJ", NULL);
	}
	Prop = FindProperty (Cur, "CJSW", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.CJSW = atof (Prop);
		ezxml_set_attr (Cur, "CJSW", NULL);
	}
	Prop = FindProperty (Cur, "CJSWG", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.CJSWG = atof (Prop);
		ezxml_set_attr (Cur, "CJSWG", NULL);
	}
	Prop = FindProperty (Cur, "CGDO", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.CGDO = atof (Prop);
		ezxml_set_attr (Cur, "CGDO", NULL);
	}
	Prop = FindProperty (Cur, "COX", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.COX = atof (Prop);
		ezxml_set_attr (Cur, "COX", NULL);
	}
	Prop = FindProperty (Cur, "EC", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.EC = atof (Prop);
		ezxml_set_attr (Cur, "EC", NULL);
	}
	FreeNode (Cur);

	Cur = FindElement (Parent, "min_transistor_size", TRUE);
	Prop = FindProperty (Cur, "length", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.min_length = power->NMOS_tx_record.min_length = atof (Prop);
		ezxml_set_attr (Cur, "length", NULL);
	}
	Prop = FindProperty (Cur, "width", TRUE);
	if (Prop != NULL)
	{
		power->PMOS_tx_record.min_width = power->NMOS_tx_record.min_width = atof (Prop);
		ezxml_set_attr (Cur, "width", NULL);
	}
	FreeNode (Cur);

	Cur = FindElement (Parent, "poly", TRUE);
	Prop = FindProperty (Cur, "Cpoly", TRUE);
	if (Prop != NULL)
	{
		power->poly_inf.Cpoly = atof (Prop);
		ezxml_set_attr (Cur, "Cpoly", NULL);
	}
	Prop = FindProperty (Cur, "poly_extension", TRUE);
	if (Prop != NULL)
	{
		power->poly_inf.poly_extension = atof (Prop);
		ezxml_set_attr (Cur, "poly_extension", NULL);
	}
	FreeNode (Cur);

	Cur = FindElement (Parent, "Vdd", TRUE);
	Tokens = GetNodeTokens (Cur);
	assert (CountTokens (Tokens) == 1);
	power->supply_voltage = atof (Tokens[0]);
	FreeTokens (&Tokens);
	FreeNode (Cur);

	Cur = FindElement (Parent, "Vswing", TRUE);
	Tokens = GetNodeTokens (Cur);
	assert (CountTokens (Tokens) == 1);
	power->swing_voltage = atof (Tokens[0]);
	FreeTokens (&Tokens);
	FreeNode (Cur);

	Cur = FindElement (Parent, "Vgs_for_leakage", TRUE);
	Tokens = GetNodeTokens (Cur);
	assert (CountTokens (Tokens) == 1);
	power->Vgs_for_leakage = atof (Tokens[0]);
	FreeTokens (&Tokens);
	FreeNode (Cur);

	Cur = FindElement (Parent, "SRAM_leakage", TRUE);
	Tokens = GetNodeTokens (Cur);
	assert (CountTokens (Tokens) == 1);
	power->SRAM_leakage = atof (Tokens[0]);
	FreeTokens (&Tokens);
	FreeNode (Cur);

	Cur = FindElement (Parent, "short_circuit_power_percentage", TRUE);
	Tokens = GetNodeTokens (Cur);
	assert (CountTokens (Tokens) == 1);
	power->short_circuit_power_percentage = atof (Tokens[0]);
	FreeTokens (&Tokens);
	FreeNode (Cur);
}

static void
ProcessSegments (
    INOUTP ezxml_t Parent,
    OUTP struct s_segment_inf **Segs,
    OUTP int *NumSegs,
    INP struct s_switch_inf *Switches,
    INP int NumSwitches,
    INP boolean timing_enabled)
{
	int i, j, length;
	const char *tmp;

	ezxml_t SubElem;
	ezxml_t Node;

	/* Count the number of segs and check they are in fact
	 * of segment elements. */
	*NumSegs = CountChildren (Parent, "segment");

	/* Alloc segment list */
	*Segs = NULL;
	if (*NumSegs > 0)
	{
		*Segs = (struct s_segment_inf *) my_malloc (*NumSegs * sizeof (struct s_segment_inf));
		memset (*Segs, 0, (*NumSegs * sizeof (struct s_segment_inf)));
	}

	/* Load the segments. */
	for (i = 0; i < *NumSegs; ++i)
	{
		Node = ezxml_child (Parent, "segment");

		/* Get segment length */
		length = 1;		/* DEFAULT */
		tmp = FindProperty (Node, "length", FALSE);
		if (tmp)
		{
			if (strcmp (tmp, "longline") == 0)
			{
				(*Segs)[i].longline = TRUE;
			}
			else
			{
				length = my_atoi (tmp);
			}
		}
		(*Segs)[i].length = length;
		ezxml_set_attr (Node, "length", NULL);

		/* Get the frequency */
		(*Segs)[i].frequency = 1;	/* DEFAULT */
		tmp = FindProperty (Node, "freq", FALSE);
		if (tmp)
		{
			/*(*Segs)[i].frequency = (int) (atof (tmp) * MAX_CHANNEL_WIDTH); PAJ */
			(*Segs)[i].frequency = atof (tmp);
		}
		ezxml_set_attr (Node, "freq", NULL);

		/* Get timing info */
		(*Segs)[i].Rmetal = 0;	/* DEFAULT */
		tmp = FindProperty (Node, "Rmetal", timing_enabled);
		if (tmp)
		{
			(*Segs)[i].Rmetal = atof (tmp);
		}
		ezxml_set_attr (Node, "Rmetal", NULL);
		(*Segs)[i].Cmetal = 0;	/* DEFAULT */
		tmp = FindProperty (Node, "Cmetal", timing_enabled);
		if (tmp)
		{
			(*Segs)[i].Cmetal = atof (tmp);
		}
		ezxml_set_attr (Node, "Cmetal", NULL);

		/* Get the type */
		tmp = FindProperty (Node, "type", TRUE);
		if (0 == strcmp (tmp, "bidir"))
		{
			(*Segs)[i].directionality = BI_DIRECTIONAL;
		}

		else if (0 == strcmp (tmp, "unidir"))
		{
			(*Segs)[i].directionality = UNI_DIRECTIONAL;
		}

		else
		{
			printf (ERRTAG "Invalid switch type '%s'.\n", tmp);
			exit (1);
		}
		ezxml_set_attr (Node, "type", NULL);

		/* Get the wire and opin switches, or mux switch if unidir */
		if (UNI_DIRECTIONAL == (*Segs)[i].directionality)
		{
			SubElem = FindElement (Node, "mux", TRUE);
			tmp = FindProperty (SubElem, "name", TRUE);

			/* Match names */
			for (j = 0; j < NumSwitches; ++j)
			{
				if (0 == strcmp (tmp, Switches[j].name))
				{
					break;	/* End loop so j is where we want it */
				}
			}
			if (j >= NumSwitches)
			{
				printf (ERRTAG "'%s' is not a valid mux name.\n", tmp);
				exit (1);
			}
			ezxml_set_attr (SubElem, "name", NULL);
			FreeNode (SubElem);

			/* Unidir muxes must have the same switch
			 * for wire and opin fanin since there is
			 * really only the mux in unidir. */
			(*Segs)[i].wire_switch = j;
			(*Segs)[i].opin_switch = j;
		}

		else
		{
			assert (BI_DIRECTIONAL == (*Segs)[i].directionality);
			SubElem = FindElement (Node, "wire_switch", TRUE);
			tmp = FindProperty (SubElem, "name", TRUE);

			/* Match names */
			for (j = 0; j < NumSwitches; ++j)
			{
				if (0 == strcmp (tmp, Switches[j].name))
				{
					break;	/* End loop so j is where we want it */
				}
			}
			if (j >= NumSwitches)
			{
				printf (ERRTAG "'%s' is not a valid wire_switch name.\n", tmp);
				exit (1);
			}
			(*Segs)[i].wire_switch = j;
			ezxml_set_attr (SubElem, "name", NULL);
			FreeNode (SubElem);
			SubElem = FindElement (Node, "opin_switch", TRUE);
			tmp = FindProperty (SubElem, "name", TRUE);

			/* Match names */
			for (j = 0; j < NumSwitches; ++j)
			{
				if (0 == strcmp (tmp, Switches[j].name))
				{
					break;	/* End loop so j is where we want it */
				}
			}
			if (j >= NumSwitches)
			{
				printf (ERRTAG "'%s' is not a valid opin_switch name.\n", tmp);
				exit (1);
			}
			(*Segs)[i].opin_switch = j;
			ezxml_set_attr (SubElem, "name", NULL);
			FreeNode (SubElem);
		}

		/* Setup the CB list if they give one, otherwise use full */
		(*Segs)[i].cb_len = length;
		(*Segs)[i].cb = (boolean *) my_malloc (length * sizeof (boolean));
		for (j = 0; j < length; ++j)
		{
			(*Segs)[i].cb[j] = TRUE;
		}
		SubElem = FindElement (Node, "cb", FALSE);
		
		if (SubElem)
		{
			(*Segs)[i].frac_cb = ProcessCB_SB (SubElem, (*Segs)[i].cb, length);
			FreeNode (SubElem);
		}

		/* Setup the SB list if they give one, otherwise use full */
		(*Segs)[i].sb_len = (length + 1);
		(*Segs)[i].sb = (boolean *) my_malloc ((length + 1) * sizeof (boolean));
		for (j = 0; j < (length + 1); ++j)
		{
			(*Segs)[i].sb[j] = TRUE;
		}
		SubElem = FindElement (Node, "sb", FALSE);
		if (SubElem)
		{
			(*Segs)[i].frac_sb = ProcessCB_SB (SubElem, (*Segs)[i].sb, (length + 1));
			FreeNode (SubElem);
		}
		FreeNode (Node);
	}
}
float
ProcessCB_SB (
    INOUTP ezxml_t Node,
    INOUTP boolean * list,
    INP int len)
{
	const char *tmp = NULL;
	int i;
	int count = 0;

	/* Check the type. We only support 'pattern' for now.
	 * Should add frac back eventually. */
	tmp = FindProperty (Node, "type", TRUE);
	if (0 == strcmp (tmp, "pattern"))
	{
		i = 0;

		/* Get the content string */
		tmp = Node->txt;
		while (*tmp)
		{
			switch (*tmp)
			{
				case ' ':
					break;
				case 'T':
				case '1':
					if (i >= len)
					{
						printf (ERRTAG "CB or SB depopulation is too long. It " "should be (length) symbols for CBs and (length+1) " "symbols for SBs.\n");
						exit (1);
					}
					list[i] = TRUE;
					++i;
					count ++;
					break;
				case 'F':
				case '0':
					if (i >= len)
					{
						printf (ERRTAG "CB or SB depopulation is too long. It " "should be (length) symbols for CBs and (length+1) " "symbols for SBs.\n");
						exit (1);
					}
					list[i] = FALSE;
					++i;
					break;
				default:
					printf (ERRTAG "Invalid character %c in CB or " "SB depopulation list.\n", *tmp);
					exit (1);
			}
			++tmp;
		}
		if (i < len)
		{
			printf (ERRTAG "CB or SB depopulation is too short. It " "should be (length) symbols for CBs and (length+1) " "symbols for SBs.\n");
			exit (1);
		}

		/* Free content string */
		ezxml_set_txt (Node, "");
	}

	else
	{
		printf (ERRTAG "'%s' is not a valid type for specifying " "cb and sb depopulation.\n", tmp);
		exit (1);
	}
	ezxml_set_attr (Node, "type", NULL);

	return ((float)count)/((float)len);
}

static void
ProcessSwitches (
    INOUTP ezxml_t Parent,
    OUTP struct s_switch_inf **Switches,
    OUTP int *NumSwitches,
    INP boolean timing_enabled)
{
	int i, j;
	const char *type_name;
	const char *switch_name;
	const char *Prop;

	boolean has_buf_size;
	ezxml_t Node;
	has_buf_size = FALSE;

	/* Count the children and check they are switches */
	*NumSwitches = CountChildren (Parent, "switch");

	/* Alloc switch list */
	*Switches = NULL;
	if (*NumSwitches > 0)
	{
		*Switches = (struct s_switch_inf *) my_malloc (*NumSwitches * sizeof (struct s_switch_inf));
		memset (*Switches, 0, (*NumSwitches * sizeof (struct s_switch_inf)));
	}

	/* Load the switches. */
	for (i = 0; i < *NumSwitches; ++i)
	{
		Node = ezxml_child (Parent, "switch");
		switch_name = FindProperty (Node, "name", TRUE);
		type_name = FindProperty (Node, "type", TRUE);

		/* Check for switch name collisions */
		for (j = 0; j < i; ++j)
		{
			if (0 == strcmp ((*Switches)[j].name, switch_name))
			{
				printf (ERRTAG "Two switches with the same name '%s' were " "found.\n", switch_name);
				exit (1);
			}
		}
		(*Switches)[i].name = my_strdup (switch_name);
		ezxml_set_attr (Node, "name", NULL);

		/* Figure out the type of switch. */
		if (0 == strcmp (type_name, "mux"))
		{
			(*Switches)[i].buffered = TRUE;
			has_buf_size = TRUE;
		}

		else if (0 == strcmp (type_name, "pass_trans"))
		{
			(*Switches)[i].buffered = FALSE;
		}

		else if (0 == strcmp (type_name, "buffer"))
		{
			(*Switches)[i].buffered = TRUE;
		}

		else
		{
			printf (ERRTAG "Invalid switch type '%s'.\n", type_name);
			exit (1);
		}
		ezxml_set_attr (Node, "type", NULL);
		Prop = FindProperty (Node, "R", timing_enabled);
		if (Prop != NULL)
		{
			(*Switches)[i].R = atof (Prop);
			ezxml_set_attr (Node, "R", NULL);
		}
		Prop = FindProperty (Node, "Cin", timing_enabled);
		if (Prop != NULL)
		{
			(*Switches)[i].Cin = atof (Prop);
			ezxml_set_attr (Node, "Cin", NULL);
		}
		Prop = FindProperty (Node, "Cout", timing_enabled);
		if (Prop != NULL)
		{
			(*Switches)[i].Cout = atof (Prop);
			ezxml_set_attr (Node, "Cout", NULL);
		}
		Prop = FindProperty (Node, "Tdel", timing_enabled);
		if (Prop != NULL)
		{
			(*Switches)[i].Tdel = atof (Prop);
			ezxml_set_attr (Node, "Tdel", NULL);
		}
		Prop = FindProperty (Node, "buf_size", has_buf_size);
		if (has_buf_size)
		{
			(*Switches)[i].buf_size = atof (Prop);
			ezxml_set_attr (Node, "buf_size", NULL);
		}
		Prop = FindProperty (Node, "mux_trans_size", FALSE);
		if (Prop != NULL)
		{
			(*Switches)[i].mux_trans_size = atof (Prop);
			ezxml_set_attr (Node, "mux_trans_size", NULL);
		}

		/* Remove the switch element from parse tree */
		FreeNode (Node);
	}
}


/* Output the data from architecture data so user can verify it
 * was interpretted correctly. */
void
EchoArch (
    INP const char *EchoFile,
    INP const t_type_descriptor * Types,
    INP int NumTypes)
{
	int i, j, k;

	FILE *Echo;
	Echo = fopen (EchoFile, "w");
	for (i = 0; i < NumTypes; ++i)
	{
		fprintf (Echo, "Type: \"%s\"\n", Types[i].name);
		fprintf (Echo, "\tcapacity: %d\n", Types[i].capacity);
		fprintf (Echo, "\theight: %d\n", Types[i].height);
		if (Types[i].num_pins > 0)
		{
			for (j = 0; j < Types[i].height; ++j)
			{
				fprintf (Echo, "\tpinloc[%d] TOP LEFT BOTTOM RIGHT:\n", j);
				for (k = 0; k < Types[i].num_pins; ++k)
				{
					fprintf (Echo, "\t\t%d %d %d %d\n", Types[i].pinloc[j][TOP][k], Types[i].pinloc[j][LEFT][k], Types[i].pinloc[j][BOTTOM][k], Types[i].pinloc[j][RIGHT][k]);
				}
			}
		}
		fprintf (Echo, "\tnum_pins (scaled for capacity): %d\n", Types[i].num_pins);
		if (Types[i].num_pins > 0)
		{
			fprintf (Echo, "\tPins: NAME CLASS IS_GLOBAL\n");
			for (j = 0; j < Types[i].num_pins; ++j)
			{
				fprintf (Echo, "\t\t%d %d %s\n", j, Types[i].pin_class[j], (Types[i].is_global_pin[j] ? "TRUE" : "FALSE"));
			}
		}
		fprintf (Echo, "\tnum_class: %d\n", Types[i].num_class);
		if (Types[i].num_class > 0)
		{
			for (j = 0; j < Types[i].num_class; ++j)
			{
				switch (Types[i].class_inf[j].type)
				{
					case RECEIVER:
						fprintf (Echo, "\t\tType: RECEIVER\n");
						break;
					case DRIVER:
						fprintf (Echo, "\t\tType: DRIVER\n");
						break;
					case OPEN:
						fprintf (Echo, "\t\tType: OPEN\n");
						break;
					default:
						fprintf (Echo, "\t\tType: UNKNOWN\n");
						break;
				}
				fprintf (Echo, "\t\t\tnum_pins: %d\n", Types[i].class_inf[j].num_pins);
				fprintf (Echo, "\t\t\tpins: ");	/* No \n */
				for (k = 0; k < Types[i].class_inf[j].num_pins; ++k)
				{
					fprintf (Echo, "%d ", Types[i].class_inf[j].pinlist[k]);	/* No \n */
				}
				fprintf (Echo, "\n");	/* End current line */
			}
		}
		fprintf (Echo, "\tis_Fc_frac: %s\n", (Types[i].is_Fc_frac ? "TRUE" : "FALSE"));
		fprintf (Echo, "\tis_Fc_out_full_flex: %s\n", (Types[i].is_Fc_out_full_flex ? "TRUE" : "FALSE"));
		fprintf (Echo, "\tFc_in: %f\n", Types[i].Fc_in);
		fprintf (Echo, "\tFc_out: %f\n", Types[i].Fc_out);
		fprintf (Echo, "\tmax_subblocks: %d\n", Types[i].max_subblocks);
		fprintf (Echo, "\tmax_subblock_inputs: %d\n", Types[i].max_subblock_inputs);
		fprintf (Echo, "\tmax_subblock_outputs: %d\n", Types[i].max_subblock_outputs);
		fprintf (Echo, "\tnum_drivers: %d\n", Types[i].num_drivers);
		fprintf (Echo, "\tnum_receivers: %d\n", Types[i].num_receivers);
		fprintf (Echo, "\tindex: %d\n", Types[i].index);
		fprintf (Echo, "\n");
	}
	fclose (Echo);
}
